<?php

declare(strict_types=1);

namespace App\Libs\Wx;

use App\Model\WxOption;

class WxSdk
{
    static array $wx_conf;

    public function __construct (string $confDef='wx_config')
    {
        self::$wx_conf = [
            'app_id' => config($confDef)['app_id'],
            'app_secret' => config($confDef)['app_secret'],
        ];
    }


    /**
     * @param string $url
     * @return array
     * @throws \Exception
     */
    static function getSignPackage(string $url)
    {
        $js_api_ticket = self::getJsApiTicket();
//        $url = $_SERVER['HTTP_REFERER'];
//        $url = 'https://p1.rp-mall.com/hanglung23/test';
        $timestamp = time();
        $nonceStr = self::createNonceStr();
        // 这里参数的顺序要按照 key 值 ASCII 码升序排序
        $string = 'jsapi_ticket=' . $js_api_ticket . '&noncestr=' . $nonceStr . '&timestamp=' . $timestamp . '&url=' . $url;
        $signature = sha1($string);
        $signPackage = array(
            "appId"     => self::$wx_conf['app_id'],
            "nonceStr"  => $nonceStr,
            "timestamp" => $timestamp,
            "url"       => $url,
            "signature" => $signature,
            "rawString" => $string
        );
        return $signPackage;
    }

    static function createNonceStr(int $length = 16): string
    {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * @return string | null
     * @throws \Exception
     */
    static function getJsApiTicket(): string | null
    {
        // jsapi_ticket 应该全局存储与更新，以下代码以写入到文件中做示例
        $data = WxOption::select('option_value')
            ->where('option_name', 'js_api_ticket')
            ->first();
        if ($data == null) {
            $accessToken = self::getAccessToken();
            $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=$accessToken&type=jsapi";
            $res_data = json_decode(self::httpGet($url));
            if ($res_data && $res_data->errcode == 0){
                $ticket = $res_data->ticket;
                $save_data = [
                    'option_name' => 'js_api_ticket',
                    'option_value' => json_encode(['time_stamp' => time() + 7200, 'js_api_ticket' => $ticket])
                ];
                WxOption::insert($save_data);
                return $ticket;
            } else {
                throw new \Exception($res_data);
            }
        }
        $js_api_ticket_info = json_decode($data->option_value);
        if ($js_api_ticket_info->time_stamp < time() || $js_api_ticket_info->js_api_ticket == '') {
            $accessToken = self::getAccessToken();
            $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=$accessToken&type=jsapi";
            $res_data = json_decode(self::httpGet($url));
            $ticket = $res_data->ticket;
            if ($ticket){
                WxOption::where('option_name', 'js_api_ticket')
                    ->update(['option_value' => json_encode(['time_stamp' => time() + 7200, 'js_api_ticket' => $ticket])]);
                return $ticket;
            } else {
                return null;
            }
        } else {
            return $js_api_ticket_info->js_api_ticket;
        }
    }

    /**
     * @return string | null
     * @throws \Exception
     */
    static function getAccessToken(): string | null
    {
        // access_token 应该全局存储与更新，以下代码以写入到文件中做示例
        $access_token_info = WxOption::select('option_value')
            ->where('option_name', 'access_token')
            ->first();

        if ($access_token_info == null) {
            $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . self::$wx_conf['app_id'] . '&secret=' . self::$wx_conf['app_secret'];
            $json = self::httpGet($url);
            $res = json_decode($json);

            if ($res || !isset($res->errcode)) {
                $token = $res->access_token;
                $save_data = [
                    'option_name' => 'access_token',
                    'option_value' => json_encode(['time_stamp' => time() + 7200, 'access_token' => $token])
                ];
                WxOption::insert($save_data);
                return $token;
            } else {
                throw new \Exception($res);
            }
        }

        $access_token_info = json_decode($access_token_info->option_value);
        if ($access_token_info->time_stamp < time() || $access_token_info->access_token == '') {
            $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . self::$wx_conf['app_id'] . '&secret=' . self::$wx_conf['app_secret'];
            $json = self::httpGet($url);
            $res = json_decode($json);
            $token = $res->access_token;
            if($token){
                WxOption::where('option_name', 'access_token')
                    ->update(['option_value' => json_encode(['time_stamp' => time() + 7200, 'access_token' => $token])]);
                return $token;
            } else {
                return null;
            }
        } else {
            return $access_token_info->access_token;
        }

    }

    static function httpGet(string $url): string
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_TIMEOUT, 500);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_URL, $url);
        $res = curl_exec($curl);
        curl_close($curl);
        return $res;
    }

    static function curl(string $url, array $data = null, string $type = 'post')
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_TIMEOUT, 500);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_URL, $url);
        if (strtolower($type) == 'post' && $data != null) {
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        $result = curl_exec($curl);
        curl_close($curl);
        return $result;
    }

}